package com.i360day.redis.aspect;

import com.alibaba.fastjson.JSON;
import com.i360day.redis.constant.RedisContextHolder;
import com.i360day.redis.constant.RedisThreadLocalContextHolder;
import com.i360day.redis.exception.RepeatSubmitException;
import com.i360day.redis.lock.RedisLock;
import com.i360day.redis.properties.GlobalRepeatSubmitProperties;
import com.i360day.redis.properties.RedisProperties;
import com.i360day.redis.util.IpUtils;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.UUID;


@ConditionalOnProperty(value = "spring.redis.i360day.globalRepeatSubmit.enabled", matchIfMissing = true)
public class GlobalRepeatSubmitAdvice implements MethodInterceptor {

    private Logger log = LoggerFactory.getLogger(GlobalRepeatSubmitAdvice.class);

    @Autowired
    private RedisProperties redisProperties;

    private String getTokenOnly() {
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();
        String token = request.getHeader(redisProperties.getTokenName());
        if (ObjectUtils.isEmpty(token)) {
            return IpUtils.getIpAddr(request);
        }
        return token;
    }

    private String getCurrentRequestId(MethodInvocation invocation) {
        Method method = invocation.getMethod();
        String clazzName = invocation.getThis().getClass().getName();
        Object[] params = invocation.getArguments();
        StringBuilder builder = new StringBuilder();
        try {
            if (params != null) {
                for (Object obj : params) {
                    builder.append(JSON.toJSONString(obj));
                }
            }
        } catch (Exception ex) {
            //如果出错后，那么就不限制访问次数
            builder.append(UUID.randomUUID().toString());
            log.error("获取参数值失败...{}", ex.getMessage());
        }
        return RedisContextHolder.getRedisKey(clazzName, method.getName(), getTokenOnly(), builder.toString());
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        try {
            return doBefore(invocation);
        } catch (Throwable throwable) {
            afterThrowing(invocation, throwable);
            throw throwable;
        } finally {
            afterReturning(invocation);
        }
    }


    public Object doBefore(MethodInvocation invocation) throws Throwable {
        GlobalRepeatSubmitProperties globalRepeatSubmitProperties = redisProperties.getGlobalRepeatSubmit();
        //当前请求唯一标识
        String requestId = getCurrentRequestId(invocation);
        RedisThreadLocalContextHolder.setObj(requestId);
        RedisLock redisLock = RedisLock.instance(requestId);
        redisLock.call(() -> {
            Integer redisValue = RedisContextHolder.get(requestId, Integer.class);
            //如果获取到的值，大于当前设置的次数则抛异常
            if (null != redisValue) {
                //重复提交
                if (redisValue > globalRepeatSubmitProperties.getLimitCount()) {
                    throw new RepeatSubmitException(StringUtils.isEmpty(globalRepeatSubmitProperties.getErrorMsg()) ? "请勿重复提交" : globalRepeatSubmitProperties.getErrorMsg());
                }
                RedisContextHolder.set(requestId, redisValue + 1, globalRepeatSubmitProperties.getTime());
            } else {
                RedisContextHolder.set(requestId, 1, globalRepeatSubmitProperties.getTime());
            }
            return redisValue;
        });
        return invocation.proceed();
    }

    public void afterReturning(MethodInvocation invocation) {
        GlobalRepeatSubmitProperties globalRepeatSubmitProperties = redisProperties.getGlobalRepeatSubmit();
        //当前请求唯一标识
        String requestId = RedisThreadLocalContextHolder.getObj(String.class);
        try {
            if (globalRepeatSubmitProperties.getAfterReturningDelete() && !ObjectUtils.isEmpty(requestId)) {
                RedisContextHolder.delete(requestId);
            }
        } finally {
            RedisThreadLocalContextHolder.clear();
        }
    }

    public void afterThrowing(MethodInvocation invocation, Throwable throwable) {
        GlobalRepeatSubmitProperties globalRepeatSubmitProperties = redisProperties.getGlobalRepeatSubmit();
        //当前请求唯一标识
        String requestId = RedisThreadLocalContextHolder.getObj(String.class);
        try {
            if (globalRepeatSubmitProperties.getAfterReturningDelete() && !ObjectUtils.isEmpty(requestId)) {
                RedisContextHolder.delete(requestId);
            }
        } finally {
            RedisThreadLocalContextHolder.clear();
        }
    }
}
